/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * $Id: configreader.cpp,v 1.7 2009-05-13 07:18:19 amaula Exp $
 *
 * \file
 * \brief Implementation file of Configuration file reader for MaCI launcher.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
//*****************************************************************************
//*****************************************************************************
#include "configreader.hpp"
#include "owndebug.h"
#include <libxml/xmlmemory.h>
#include <libxml/parser.h> 
#include <string>
#include <assert.h>
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

bool CConfigReader::ReadConfig(SMachineConfEntry &aEntry, const std::string &aFilename)
{
  xmlDocPtr docp;
  xmlNodePtr nodep;
  bool result = false;

  // Open the config file
  docp = xmlReadFile(aFilename.c_str(), NULL, 0);
  if(docp == NULL){
    dPrint(1, "Failed to open settings file '%s'!", aFilename.c_str());
    return false;
  }
  
  // Get root element
  nodep = xmlDocGetRootElement(docp);
  assert(nodep != NULL);

  // Check that we have a Machine
  if( xmlStrcmp(nodep->name, (xmlChar *) "machine")) {
    dPrint(1, "This is not a MaCI-Machine configuration file. (Invalid root node: '%s')",
           nodep->name);
    
  } else {
    
    // Check version.
    xmlChar *machine_version = xmlGetProp(nodep, (xmlChar *) "version");
    if (machine_version) {
      dPrint(2,"Machine definition version '%s'", machine_version);
      xmlFree(machine_version);
      machine_version = NULL;
      
    } else {
      dPrint(2,"Unknown machine definition version. (Version property not set)");
      
    }

    // Clear the output
    aEntry.Clear();

    // Enter children level of root.
    nodep = nodep->children;
    
    // parse 
    while(nodep) {
      if (nodep->type == XML_ELEMENT_NODE) {
        if ( !xmlStrcmp(nodep->name, (xmlChar *) "name") ) {
          // Name node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.name = (char*)content;
            dPrint(2,"Got name: '%s'", content);
            xmlFree(content);
          }

        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "description") ) {
          // Description node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.description = (char*)content;
            dPrint(2,"Got description: '%s'", content);
            xmlFree(content);
          }
            
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "MaCI") ) {
          // MaCI node - traverse down the tree and continue.
          xmlNodePtr subnodep = nodep->children;
          while(subnodep) {

            if (subnodep->type == XML_ELEMENT_NODE) {
              if ( !xmlStrcmp(subnodep->name, (xmlChar *) "Group") ) {
                // MaCI::Group node
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.MaCIGroup = (char*)content;
                  dPrint(2,"Got MaCI-Group: '%s'", content);
                  xmlFree(content);
                }

              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "GIMnetAP_host") ) {
                // GIMnet_host
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.GIMnet_host = (char*)content;
                  dPrint(2,"Got GIMnet_host: '%s'", content);
                  xmlFree(content);
                  
                } else {
                  dPrint(2,"Using Commandline conf for GIMnet_host");
                  
                }
                
              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "GIMnetAP_port") ) {
                // GIMnet_port
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.GIMnet_port = atoi((char*)content);
                  dPrint(2,"Got GIMnet_port: '%d'", atoi((char*)content));
                  xmlFree(content);
                  
                } else {
                  dPrint(2,"Using Commandline conf for GIMnet_port");
                  
                }
                
              } else {
                dPrint(2,"Unknown Machine-MaCI parameter: '%s'", 
                       subnodep->name);

              }
            }  else {
              //dPrint(10,"Sub-Element was not XML_ELEMENT_NODE?");
              
            }
            
            // Traverse next
            subnodep = subnodep->next;
          }
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "workdir") ) {
           // Workdir node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.workdir = (char*)content;
            dPrint(2,"Got workdir: '%s'", content);
            xmlFree(content);
          }

        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "modules") ) {
          // Modules node
             // MaCI node - traverse down the tree and continue.
          xmlNodePtr subnodep = nodep->children;
          while(subnodep) {

            if (subnodep->type == XML_ELEMENT_NODE) {
              if ( !xmlStrcmp(subnodep->name, (xmlChar *) "file") ) {
                // 'File' node
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.moduleFiles.push_back((char*)content);
                  dPrint(2,"Got Module file: '%s'", content);
                  xmlFree(content);
                }

              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "pattern") ) {
                // 'Pattern' node
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.modulePatterns.push_back((char*)content);
                  dPrint(2,"Got Module pattern: '%s'", content);
                  xmlFree(content);
                }
                
              } else {
                dPrint(2,"Unknown Machine-MaCI parameter: '%s'", 
                       subnodep->name);

              }
            }  else {
              //dPrint(10,"Sub-Element was not XML_ELEMENT_NODE?");
              
            }
            
            // Traverse next
            subnodep = subnodep->next;
          }
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "extbin") ) {
          // extbin node
          // MaCI node - traverse down the tree and continue.
          xmlNodePtr subnodep = nodep->children;
          while(subnodep) {
            
            if (subnodep->type == XML_ELEMENT_NODE) {
              if ( !xmlStrcmp(subnodep->name, (xmlChar *) "file") ) {
                // 'File' node
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.extbinFiles.push_back((char*)content);
                  dPrint(2,"Got ExtBin file: '%s'", content);
                  xmlFree(content);
                }

              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "pattern") ) {
                // 'Pattern' node
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.extbinPatterns.push_back((char*)content);
                  dPrint(2,"Got ExtBin pattern: '%s'", content);
                  xmlFree(content);
                }
                
              } else {
                dPrint(2,"Unknown Machine-MaCI parameter: '%s'", 
                       subnodep->name);

              }
            }  else {
              //dPrint(10,"Sub-Element was not XML_ELEMENT_NODE?");
              
            }
            
            // Traverse next
            subnodep = subnodep->next;
          }
        } else {
          dPrint(2,"Unknown machine parameter: '%s'", nodep->name);
          
        }
        
      } else {
        //dPrint(10,"Element was not XML_ELEMENT_NODE?");
        
      }
      
      // Traverse to next node in level.
      nodep = nodep->next;
    }

    // Got here; so mark result ok.
    result = true;
  }

  // Free doc.
  if (docp) xmlFreeDoc(docp);

  return result;
}
//*****************************************************************************

bool CConfigReader::ReadConfig(SModuleConfEntry &aEntry, const std::string &aFilename)
{
  xmlDocPtr docp;
  xmlNodePtr nodep;
  bool result = false;
  bool is_external_module = false; // Flag whether this is Ext Module.

  // Open the config file
  docp = xmlReadFile(aFilename.c_str(), NULL, 0);
  if(docp == NULL){
    dPrint(1, "Failed to open settings file '%s'!", aFilename.c_str());
    return false;
  }  
   
  // Get root element
  nodep = xmlDocGetRootElement(docp);
  assert(nodep != NULL);
  
  // Check that we have a module
  if( xmlStrcmp(nodep->name, (xmlChar *) "module") &&
      xmlStrcmp(nodep->name, (xmlChar *) "extmodule")) {
    dPrint(1, "This is not a MaCI-Module configuration file. (Invalid root node: '%s')",
           nodep->name);
    
  } else {
    
    // Check whether this is ext module -> Mark as external.
    if (!xmlStrcmp(nodep->name, (xmlChar *) "extmodule")) {
      is_external_module = true;
    }
    
    // Check version.
    xmlChar *module_version = xmlGetProp(nodep, (xmlChar *) "version");
    if (module_version) {
      dPrint(2,"Module definition version '%s'", module_version);
      xmlFree(module_version);
      module_version = NULL;
      
    } else {
      dPrint(2,"Unknown module definition version. (Version property not set)");
      
    }
    
    // Clear the output
    aEntry.Clear();
    
    // Enter children level of root.
    nodep = nodep->children;
    
    // parse 
    while(nodep) {
      if (nodep->type == XML_ELEMENT_NODE) {
        if ( !xmlStrcmp(nodep->name, (xmlChar *) "name") ) {
          // Name node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.name = (char*)content;
            dPrint(2,"Got name: '%s'", content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "description") ) {
          // Description node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.description = (char*)content;
            dPrint(2,"Got description: '%s'", content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "executable") ) {
          // Executable node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.executablePath = (char*)content;
            dPrint(2,"Got executablePath: '%s'", content);
            xmlFree(content);
          }

        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "build") ) {
          // build command node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.buildCommand = (char*)content;
            dPrint(2,"Got build-Command: '%s'", content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "workdir") ) {
          // workdir node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.workingDirectory = (char*)content;
            dPrint(2,"Got workingDirectory: '%s'", content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "flags") ) {
          // flags node - traverse down the tree and continue.
          xmlNodePtr subnodep = nodep->children;
          while(subnodep) {
            
            if (subnodep->type == XML_ELEMENT_NODE) {
              if ( !xmlStrcmp(subnodep->name, (xmlChar *) "ignore_param") ) {
                // ignore_param internals, get contents
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  // Take the contents to std::string
                  const std::string flag((const char*)content);
                  // Check flag type.
                  if (flag == "GIMnet") {
                    aEntry.flags |= KModuleFlagIgnoreGIMnetParam;
                    dPrint(3,"Flag: Ignoring GIMnet parameters");

                  } else if (flag == "MaCI") {
                    aEntry.flags |= KModuleFlagIgnoreMaCIParam;
                    dPrint(3,"Flag: Ignoring MaCI parameters");

                  } else if (flag == "ModuleConf") {
                    aEntry.flags |= KModuleFlagIgnoreModuleConfigParam;
                    dPrint(3,"Flag: Ignoring ModuleConf parameters");
                    
                  } else if (flag == "Extras") {
                    aEntry.flags |= KModuleFlagIgnoreExtraParam;
                    dPrint(3,"Flag: Ignoring Extra parameters");

                  } else {
                    dPrint(2,"Unrecognized ignore flag '%s', Ignoring",
                           flag.c_str());
                  }

                  // Free contents.
                  xmlFree(content);
                  
                } else {
                  dPrint(2,"Empty flags field. Ignoring.");
                  
                }
              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "drop_root_privileges") ) {
                // Need root.
                aEntry.flags |= KModuleFlagDropRootPrivileges;
                dPrint(3,"Flag: Drop root privileges");
                
              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "respawn_process") ) {
                // Will respawn process.
                aEntry.flags |= KModuleFlagRespawnProcess;

                // Get delay (if set)
                xmlChar *delay_ms = xmlGetProp(subnodep, (xmlChar *) "delay_ms");
                if (delay_ms) {
                  aEntry.respawnTime_ms = atoi((char *)delay_ms);
                  xmlFree(delay_ms);
                  delay_ms = NULL;
                }
                dPrint(3,"Flag: Respawn process (Delay: %u ms)", aEntry.respawnTime_ms);
                
              } else {
                dPrint(2,"Unrecognized flag type '%s', Ignoring!", 
                       subnodep->name);
                
              }
            }

            // Traverse next subnode.
            subnodep = subnodep->next;
          } 
         
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "MaCI") ) {
          // MaCI node - traverse down the tree and continue.
          xmlNodePtr subnodep = nodep->children;
          while(subnodep) {
            
            if (subnodep->type == XML_ELEMENT_NODE) {
              if ( !xmlStrcmp(subnodep->name, (xmlChar *) "Group") ) {
                // Group node
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.MaCIGroup = (char*)content;
                  dPrint(2,"Got MaCI-Group: '%s'", content);
                  xmlFree(content);

                } else {
                  dPrint(2,"Using Autoconf for MaCI-Group");

                }
                  
              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "GIMnetAP_host") ) {
                // GIMnet_host
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.GIMnet_host = (char*)content;
                  dPrint(2,"Got GIMnet_host: '%s'", content);
                  xmlFree(content);

                } else {
                  dPrint(2,"Using Autoconf for GIMnet_host");

                }

              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "GIMnetAP_port") ) {
                // GIMnet_port
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.GIMnet_port = atoi((char*)content);
                  dPrint(2,"Got GIMnet_port: '%d'", atoi((char*)content));
                  xmlFree(content);

                } else {
                  dPrint(2,"Using Autoconf for GIMnet_port");

                }
                
              } else if ( !xmlStrcmp(subnodep->name, (xmlChar *) "GIMnet_name") ) {
                // GIMnet_port
                xmlChar *content = xmlNodeListGetString(docp, subnodep->xmlChildrenNode, 0);
                if (content) {
                  aEntry.GIMnet_name = (char*)content;
                  dPrint(2,"Got GIMnet_name: '%s'", content);
                  xmlFree(content);

                } else {
                  dPrint(2,"Using Autoconf for GIMnet_name");

                }

              } else {
                dPrint(3,"Unknown MaCI parameter: '%s'", 
                       subnodep->name);
                
              }
            }  else {
              //dPrint(10,"Sub-Element was not XML_ELEMENT_NODE?");
              
            }
            
            // Traverse next
            subnodep = subnodep->next;
          }

        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "moduleconf") ) {
          // Description node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.moduleConf = (char*)content;
            dPrint(2,"Got moduleconf: '%s'", content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "startdelay") ) {
           // Description node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            aEntry.startdelay = atoi((char*)content);
            dPrint(2,"Got startdelay: '%s'", content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "param") ) {
           // Description node
          xmlChar *content = xmlNodeListGetString(docp, nodep->xmlChildrenNode, 0);
          if (content) {
            dPrint(2,"Got param(%d): '%s'", aEntry.moduleParam.size(), content);
            aEntry.moduleParam.push_back((char*)content);
            xmlFree(content);
          }
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "group") ) {
          dPrint(3,"CSettings compatible 'group' tag found. Leaving alone.");

        } else {
          dPrint(3,"Unknown module parameter: '%s'", nodep->name);
          
        }
        
      } else {
        //dPrint(10,"Element was not XML_ELEMENT_NODE?");
        
      }
      
      // Traverse to next node in level.
      nodep = nodep->next;
    }

    // Result is true when document was parsed ok in the first place.
    result = true;
  }

  // Free doc.
  if (docp) xmlFreeDoc(docp);
  
  return result;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
